using System;
using System.Collections.Generic;
using System.Globalization;

using Atomic.Libraries;
using Atomic.Libraries.Mathematics;
using Atomic.Libraries.Plotting;

namespace Atomic.Thermodynamics.Phonons
{
	[Serializable]
	public class PhononDispersion
	{
		public PhononDispersion(IEnumerable<PhononDispersionBranch> branches)
		{
			if (branches == null)
			{
				throw new ArgumentNullException();
			}

			Branches = new ReadOnlyList<PhononDispersionBranch>(branches);
		}

		public PhononDispersion(IEnumerable<PhononDispersionBranch> branches, IEnumerable<PhononDispersionSymmetryPoint> symmetryPoints)
			: this(branches)
		{
			if (symmetryPoints == null)
			{
				throw new ArgumentNullException();
			}

			SymmetryPoints = new ReadOnlyList<PhononDispersionSymmetryPoint>(symmetryPoints);
		}

		public PhononDispersion(IEnumerable<PhononDispersionBranch> branches, IEnumerable<SpaceVector> symmetryPoints)
			: this(branches, CreateSymmetryPoints(branches, symmetryPoints))
		{
		}

		private static IEnumerable<PhononDispersionSymmetryPoint> CreateSymmetryPoints(IEnumerable<PhononDispersionBranch> branches, IEnumerable<SpaceVector> symmetryPoints)
		{
			foreach (SpaceVector symmetryPoint in symmetryPoints)
			{
				// Find closest point.
				double distance0 = double.PositiveInfinity;
				PhononDispersionPoint point0 = null;
				foreach (PhononDispersionBranch branch in branches)
				{
					foreach (PhononDispersionPoint point in branch.Points)
					{
						double distance = SpaceVector.Norm(symmetryPoint - point.ReciprocalPosition.Coordinates);
						if (distance < distance0)
						{
							distance0 = distance;
							point0 = point;
						}
					}
				}

				if (point0 == null || distance0 > 1.0e-6)
				{
					throw new Exception("Could find reciprocal high-symmetry point.");
				}

				yield return new PhononDispersionSymmetryPoint(point0.ReciprocalPosition, point0.Distance, null);
			}
		}

		public void Plot()
		{
			double d = 0.0;
			double f = 0.0;
			foreach (PhononDispersionBranch branch in Branches)
			{
				foreach (PhononDispersionPoint point in branch.Points)
				{
					d = Math.Max(d, point.Distance);
					f = Math.Max(f, point.Frequency);
				}
			}

			// A crude plot of the dispersion curves.
			Gnuplot p = new Gnuplot();
			if (SymmetryPoints != null)
			{
				foreach (PhononDispersionSymmetryPoint point in SymmetryPoints)
				{
					p.Add(string.Format(CultureInfo.InvariantCulture, "set arrow from {0},0 to {0},{1} nohead", point.Distance, f));
				}
			}
			p.SetXRange(0.0, d);
			p.SetYRange(0.0, f);
			throw new NotImplementedException();
			//p.Plot(new List<PhononDispersionBranch>(Branches).ConvertAll<FunctionPlot>(g => new FunctionPlot(g, 0.0, d)));
			//p.Pause();
			//p.Run();
		}

		/// <summary>
		/// Branches corresponding to the number of atoms of the primitive cell; analogous to bands for Bloch electrons.
		/// </summary>
		public ReadOnlyList<PhononDispersionBranch> Branches
		{
			// Branches seems to be the universally accepted name for phonons for the same concept
			// as bands for electrons (cf. Bruus-Flensberg p. 57 and Fultz p. 7).
			get;
			private set;
		}

		public ReadOnlyList<PhononDispersionSymmetryPoint> SymmetryPoints
		{
			get;
			private set;
		}
	}
}
